#pragma once

#include "BasePlayer.h"

template<typename T>
class Room {
public:
    Room() {}
    virtual ~Room() {
        m_players.clear();
    }
public:
    virtual std::shared_ptr<T> playerEnter() {
        std::lock_guard<std::recursive_mutex> l(this->m_mutex);
        uint8_t seat = (uint8_t)this->m_players.size();
        if (this->m_maxPlayerCount > seat) {
            std::shared_ptr<T> ret(new T(), [](T* ptr){
                delete ptr;
            });
            ret->setSeat(seat);// seat 从0开始
            this->m_players.emplace_back(ret);
            return ret;
        }
        return nullptr;
    }
    virtual void playerLeave(uint8_t seat) {
        // 有人走了怎么办?
    }
    virtual void playerOffline(uint8_t seat) {
        // 有人掉线怎么办?
    }
    std::shared_ptr<T> getPlayer(uint8_t seat) {
        std::lock_guard<std::recursive_mutex> l(this->m_mutex);
        if (seat >= 0 && seat < this->m_players.size()) {
            return m_players[seat];
        }
        return nullptr;
    }
    uint8_t getRealSeat(uint8_t seat, int offset) const {
        size_t playerCount = this->m_players.size();
        if (offset >= 0) {
            return (seat + offset) % playerCount;
        }
        return (playerCount - ((-offset) % playerCount) + seat) % playerCount;
    }
    DEFINE_PROPERTY(uint8_t, m_masterSeat, MasterSeat);         // 誰是庄?
    DEFINE_PROPERTY(uint8_t, m_maxPlayerCount, MaxPlayerCount); // 限制只能有几人参与牌局?
protected:
    std::recursive_mutex m_mutex;
    std::vector< std::shared_ptr<T> > m_players; //玩家列表
};

template<typename T>
class BaseLogic : public Room<T>
{
public:
    BaseLogic() : m_cardCursor(0), m_waitTime(30) {
    }
    virtual ~BaseLogic() {
    }

public:
    virtual std::shared_ptr<T> playerEnter() {
        std::shared_ptr<T> ret = Room<T>::playerEnter();
        if(ret != nullptr) {
            ret->addActionListener([this](const T * const thePlayer, PLAYER_STATE state, int32_t card) {
                std::lock_guard<std::recursive_mutex> l(this->m_mutex);
                uint8_t seat = thePlayer->getSeat();
                if (state == PLAYER_STATE::ON_CARD_OUT_OVER && card > 0) {
                    DebugLogWrite("出牌结束: %u\n---------------------------\n", seat);
                    // 重置倒计时时钟
                    this->m_activeSeat = this->getRealSeat(seat, 1);
                    this->m_countdown = this->m_waitTime;
                    // 激活下一位
                    auto player = this->getPlayer(this->m_activeSeat);
                    DebugLogWrite("出牌开始: %u\n", player->getSeat());
                    player->cardOut();
                }
            });
        }
        return ret;
    }
    virtual void shuffle() {
        // 洗牌, 别人洗牌不放心, 自己写个交换规则看着舒服, 子类可以继承自己写自己的
        std::lock_guard<std::recursive_mutex> l(this->m_mutex);
        std::random_device rd;
        int32_t cardCount = (int32_t)this->m_cards.size();
        for (int i = 0; i < 100; ++i) {
            std::swap(this->m_cards[0], this->m_cards[rd() % cardCount]);
            std::swap(this->m_cards[cardCount - 1], this->m_cards[rd() % cardCount]);
            std::swap(this->m_cards[rd() % cardCount], this->m_cards[rd() % cardCount]);
        }
        m_cardCursor = 0;
    }
    virtual void deal(uint16_t count, float delay = 0, std::function<void()> onFinish = nullptr) {
        // 每人发 count 张牌, 每张牌间隔 delay 秒, 发牌完成之后调用 onFinish
        std::lock_guard<std::recursive_mutex> l(this->m_mutex);
        size_t playerCount = this->m_players.size();
        assert(this->m_cards.size() >= playerCount * count);
        if (delay < 0.001f) {
            // 没有延迟的话直接循环发牌
            for (size_t i = 0; i < count * playerCount; ++i) {
                this->m_players[i % playerCount]->cardIn(this->popCard());
            }
            if (onFinish) {
                onFinish();
            }
        } else {
            // 带延迟模式的发牌
            DelayCall::add([this, playerCount, onFinish](int tick, bool last, int& count) {
                std::lock_guard<std::recursive_mutex> l(this->m_mutex);
                this->m_players[tick % playerCount]->cardIn(this->popCard());
                if (last && onFinish) {
                    onFinish();
                }
            }, delay, (int)(count * playerCount));
        }
    }
    int32_t popCard() {
        std::lock_guard<std::recursive_mutex> l(this->m_mutex);
        // 发一张牌
        if(this->m_cardCursor < this->m_cards.size()) {
            return this->m_cards[this->m_cardCursor++];
        }
        return -1;
    }
    // 一切重新开始
    virtual void init() {
        this->m_players.clear();
        this->m_cardCursor = 0;
        this->m_countdown = 0;
        this->m_gameRunning = false;
    }
    virtual uint8_t getActiveSeat() const {
        return m_activeSeat;// 该谁出牌？
    }
    virtual void start() {
        // 庄先出牌
        std::lock_guard<std::recursive_mutex> l(this->m_mutex);
        auto player = this->getPlayer(this->m_masterSeat);
        this->m_gameRunning = true;
        this->m_activeSeat = this->m_masterSeat;
        this->m_countdown = this->m_waitTime;
        DebugLogWrite("出牌开始: %u\n", player->getSeat());
        player->cardOut();
    }
    virtual void tick(float delta) {
        if(this->m_gameRunning && this->m_countdown > 0.1) {
            this->m_countdown -= delta;
            if(this->m_countdown < 0.05) {
                this->m_countdown = 0;
            }
        }
    }
    virtual double getCountdown() const {
        return this->m_countdown;
    }
    // 牌局结算
    virtual void over() {
        this->m_gameRunning = false;
    }
    // 不换人再来一局
    virtual void onceMore() {
        std::lock_guard<std::recursive_mutex> l(this->m_mutex);
        this->m_cardCursor = 0;
        this->m_countdown = 0;
        this->m_gameRunning = false;
    }

    DEFINE_PROPERTY(bool, m_gameRunning, GameRunning);
    DEFINE_PROPERTY(int16_t, m_waitTime, WaitTime);     // 出牌最大等待时间，超过按掉线论处
protected:
    uint8_t m_activeSeat;   //该谁出牌？
    double  m_countdown;    //出牌倒计时，以 m_waitTime 为初始值倒计时
    size_t  m_cardCursor;
    std::vector< int32_t > m_cards;// 无论扑克还是麻将,每张牌都应该能用整数标识出来
};

